﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Data;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class XfcrcRecord : BiffRecord
    {
        class Crc32
        {
            static uint[] Cache
            {
                get
                {
                    if (_cache == null)
                    {
                        _cache = new uint [256];
                        for (uint Index = 0; Index < 256; Index++)
                        {
                            uint Value = Index;
                            Value = Value << 24;
                            for (int bit = 0; bit < 8; bit++)
                            {
                                uint b = 0x80000000;
                                //\\b = 0x40000000;
                                if ((Value & b) != 0)
                                {
                                    Value = Value << 1;
                                    Value = Xor(Value, 0xAF);
                                   // Value = Value | 0xAF; //\\ should be XRO
                                }
                                else
                                {
                                    Value = Value << 1;
                                }
                            }
                            Value = Value & 0xFFFF;
                            _cache[Index] = Value;
                        }
                        Contract.Assert(Enumerable.Count(Enumerable.Distinct(_cache)) > 10);
                        _cache = _cache;
                    }
                    return _cache;
                }
            }

            public uint CrcValue{get{
                return _crcValue;}}
            static uint Crc  (uint crcValue, byte b)
            {
                var Index = crcValue;
                Index = Index >> 24;
                Index = Xor(Index, b);
                crcValue = crcValue << 8;
                crcValue = Xor(crcValue, Cache[Index]);
                return crcValue;
            }

            private static uint Xor(uint a, uint b)
            {
                var result =  AND(OR(a, b), NOT(AND(a, b)));
                Contract.Assert(result == (a ^ b));
                return result;
            }

            private static uint AND(uint a, uint b)
            {
                return a & b;
            }
            private static uint OR(uint a, uint b)
            {
                return a | b;
            }
            private static uint NOT(uint a)
            {
                return ~a;
            }

            private static uint[] _cache;
            private uint _crcValue;
            public void Accumulate (byte b)
            {
                _crcValue = Crc(_crcValue, b);
            }
        }
        private uint crc;
        private ushort cxfs;
        private FrtHeader frtHeader;
        private ushort reserved;

        public XfcrcRecord(List<XFRecord> xfs) : this()
        {
            Contract.Assert(xfs != null);
            cxfs = (ushort) xfs.Count;
            SetCrc(xfs);
        }

        public void SetCrc(IEnumerable<XFRecord> xfs)
        {
            Crc32 crc32 = new Crc32();
            foreach (var xf in xfs)
            {
                foreach (var b in xf.GetBytes())
                {
                    crc32.Accumulate(b);
                }
            }
            this.crc = crc32.CrcValue;
        }

        public XfcrcRecord()
        {
            frtHeader._rt = 0x87C;
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.XFCRC; }
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
            frtHeader = new FrtHeader();
            frtHeader.Read(context.Reader);
            Contract.Assert(frtHeader._rt == 0x87C);
            reserved = context.ReadUInt16();
            cxfs = context.ReadUInt16();
            crc = context.ReadUInt32();
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
            Contract.Assert(frtHeader._rt == 0x87C);
            frtHeader.Write(context);
            context.Write(reserved);
            context.Write(cxfs);
            context.Write(crc);
        }
    }
}